/**
 * Copyright(c) 2014 Xunton Solutions
 *
 * History:
 *   14-12-23 下午2:46 Created by dwzhang
 */
package com.jade.bss.beacon.impl;

import java.util.*;
import javax.ejb.TransactionAttribute;
import javax.inject.Inject;
import javax.inject.Named;

import com.jade.bss.beacon.*;
import com.jade.bss.beacon.location.Location;
import com.jade.framework.base.criterion.Criteria;
import com.jade.framework.base.criterion.Restrictions;
import com.jade.framework.base.util.PagingList;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.transaction.annotation.Transactional;

/**
 * Beacon管理器实现。
 *
 * @author <a href="mailto:dwzhang@xunton.com">dwzhang</a> 
 * @version 1.0 14-12-23 下午2:46
 */
@Named ("bss_beaconManager")
public class BeaconManagerImpl
        implements BeaconManager
{
    private BeaconDao beaconDao;
    private BeaconListener beaconListener;
    private Log log = LogFactory.getLog(BeaconManagerImpl.class);

    @Inject
    public void setBeaconDao(@Named ("bss_beaconDao") BeaconDao beaconDao)
    {
        this.beaconDao = beaconDao;
    }

    public void setBeaconListener(BeaconListener beaconListener)
    {
        this.beaconListener = beaconListener;
    }

    public String generateUUID()
    {
        return UUID.randomUUID().toString();
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public Beacon createBeacon(Beacon beacon)
            throws BeaconException
    {
        if (StringUtils.isNotBlank(beacon.getProximityUUID())) {
            //check uuid, major, minor
            if (getBeaconByUUID(beacon.getProximityUUID(), beacon.getMajor(), beacon.getMinor()) != null) {
                throw new BeaconException(BeaconConstants.ERR_BEACON_ID_EXISTED,
                        "beacon is already exsited", null, beacon.getProximityUUID());
            }
        }


        Date now = new Date();
        beacon.setCreationTime(now);
        beacon.setLastUpdateTime(now);
        //todo set owner 相关的信息，例如lng、lat等等
        //todo save beacon logo
        beaconDao.addBeacon(beacon);

        // 更新 extend info
        Map<Class, BeaconExtInfo> infos = beacon.getInfos();
        if (infos != null && !infos.isEmpty()) {
            Set<Class> keys = infos.keySet();
            for (Class key : keys) {
                BeaconExtInfo info = infos.get(key);
                info.setBeaconId(beacon.getId());
                beaconDao.addInfo(info);
            }
        }
        return beacon;
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void updateBeacon(Beacon beacon)
            throws BeaconException
    {
        //todo update picture
        //get old beacon
        Beacon old = getBeacon(beacon.getId());
        if (old == null) {
            throw new BeaconException(BeaconConstants.ERR_BEACON_NOT_FOUND,
                    "beacon not found", null, beacon.getId());
        }

        //check beacon
        Date now = new Date();
        beacon.setLastUpdateTime(now);
        beaconDao.updateBeacon(beacon);
        // 更新 extend info
        Map<Class, BeaconExtInfo> infos = beacon.getInfos();
        if (infos != null && !infos.isEmpty()) {
            Set<Class> keys = infos.keySet();
            for (Class key : keys) {
                BeaconExtInfo info = infos.get(key);
                info.setBeaconId(beacon.getId());
                beaconDao.updateInfo(info);
            }
        }
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void updateBeacon(List<Beacon> beacons)
            throws BeaconException
    {
        for (Beacon beacon : beacons) {
            updateBeacon(beacon);
        }
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void updateApp(String appType, long appId, long... ids)
            throws BeaconException
    {
        for (long id : ids) {
            Beacon beacon = getBeacon(id);
            beacon.setAppType(appType);
            beacon.setAppId(appId);
            updateBeacon(beacon);
        }

    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void reportBeacon(Beacon beacon)
            throws BeaconException
    {
        long id = beacon.getId();
        String uuid = beacon.getProximityUUID();
        Beacon old;
        if (id != 0) {
            old = getBeacon(id);
        }
        else {
            old = getBeaconByUUID(uuid, beacon.getMajor(), beacon.getMinor());
        }
        if (old == null) {
            throw new BeaconException(BeaconConstants.ERR_BEACON_NOT_FOUND,
                    "beacon not found", null, beacon.getId());
        }
        if (id > 0 && StringUtils.isNotBlank(uuid)) {
            old.setProximityUUID(uuid);
        }

        if (old.getStatus() == BeaconConstants.STATUS_INACTIVE) {
            old.setStatus(BeaconConstants.STATUS_ACTIVE);
        }
        old.setLastUpdateTime(new Date());
        old.setTransmitPower(beacon.getTransmitPower());
        old.setTransmitFrequency(beacon.getTransmitFrequency());
        old.setBatchNo(beacon.getBatchNo());
        old.setBatteryLevel(beacon.getBatteryLevel());
        //todo caculate
        old.setBatteryLevelInDays(0);
        old.setBatteryType(beacon.getBatteryType());
        old.setHardwareVersion(beacon.getHardwareVersion());
        old.setFirmwareVersion(beacon.getFirmwareVersion());
        old.setMeasuredPower(beacon.getMeasuredPower());
        old.setPowerMode(beacon.getPowerMode());
        old.setLat(beacon.getLat());
        old.setLng(beacon.getLng());
        old.setMacAddress(beacon.getMacAddress());
        old.setMajor(beacon.getMajor());
        old.setMinor(beacon.getMinor());
        beaconDao.updateBeacon(old);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void updateStatus(long id, int status)
            throws BeaconException
    {
        int count = beaconDao.updateStatus(id, status);
        if (count == 0) {
            throw new BeaconException(BeaconConstants.ERR_BEACON_NOT_FOUND,
                    "beacon not found", null, id);
        }
    }

    public Beacon getBeacon(long id)
    {
        return beaconDao.getBeacon(id);
    }

    public Beacon getBeaconByUUID(String uuid, int major, int minor)
    {
        return beaconDao.getBeacon(uuid, major, minor);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Throwable.class)
    public void removeBeacon(long... ids)
            throws BeaconException
    {
        for (long id : ids) {
            beaconDao.removeBeacon(id);
        }
    }

    public List<Beacon> listBeacon(String ownerType, String ownerId, int status)
    {
        Criteria criteria = new Criteria(-1, -1);
        criteria.add(Restrictions.eq("ownerType", ownerType));
        criteria.add(Restrictions.eq("ownerId", ownerId));
        if (status != BeaconConstants.STATUS_ALL) {
            criteria.add(Restrictions.eq("status", status));
        }
        return beaconDao.listBeacon(criteria);
    }

    public List<Beacon> listBeacon(Criteria criteria)
    {
        return beaconDao.listBeacon(criteria);
    }

    public PagingList<Beacon> queryBeacon(Criteria criteria)
    {
        return beaconDao.queryBeacon(criteria);
    }

    public PagingList<Beacon> queryBeacon(Criteria criteria,Class... infos){
        return beaconDao.queryBeacon(criteria,infos);
    }

    public <Info extends BeaconExtInfo> Info getInfo(long beaconId, Class infoClass)
    {
        return beaconDao.getInfo(beaconId,infoClass);
    }

    public Beacon getBeacon(String uuid,int major,int minor){
        return beaconDao.getBeacon(uuid,major,minor);
    }

    public Location handleEvent(BeaconEvent event)
            throws BeaconException
    {
//        //用于后期统计
//        BeaconEventRecord record = new BeaconEventRecord();
//        record.setEventTime(event.getCreationTime());
//        record.setEventType(event.getEventType());
//        record.setDistance(event.getRegions()[0].getDistance());
//        record.setRssi(event.getRegions()[0].getRssi());
//        record.setProximityUuid(event.getRegions()[0].getUuid());
//        record.setMajor(event.getRegions()[0].getMajor());
//        record.setMinor(event.getRegions()[0].getMinor());
//        record.setProximity(event.getRegions()[0].getProximity());
//        record.setDeviceNo(event.getDeviceNo());
//        record.setUserId(event.getUserId());
//        record.setSuccess(false);

        //get beacon
//        for (BeaconRegion region : event.getRegions()) {
//            Beacon beacon = getBeaconByUUID(region.getUuid(), region.getMajor(), region.getMinor());
//            if (beacon == null) {
//                throw new BeaconException(BeaconConstants.ERR_BEACON_NOT_FOUND,
//                        "beacon not found", null, region.getUuid());
//            }
//            region.setId(beacon.getId());
//        }
        if (beaconListener == null) {
            throw new BeaconException(BeaconConstants.ERR_LISTENER_NOT_SET,
                    "Listener not set, discard the event:" + event.getDeviceNo());
        }
        return beaconListener.onBeacon(event);
    }

    protected void addBeaconEventRecord(BeaconEventRecord record)
    {
        try {
            beaconDao.addBeaconEvent(record);
        }
        catch (Throwable t) {
            log.warn("Can't add beacon event record:", t);
        }
    }

    public void removeInfo(BeaconExtInfo info)
            throws BeaconException
    {
        beaconDao.removeInfo(info);
    }

    public long getBeaconCount(Criteria criteria)
    {
        return beaconDao.getBeaconCount(criteria);
    }
}